#include <jni.h>
#include <string>
#include "SoundTouch.h"

extern "C"
{
    #include <SLES/OpenSLES.h>
    #include <SLES/OpenSLES_Android.h>
}

#include <android/log.h>
#define LOGI(FORMAT,...) __android_log_print(ANDROID_LOG_INFO,"ywl5320",FORMAT,##__VA_ARGS__);
#define LOGE(FORMAT,...) __android_log_print(ANDROID_LOG_ERROR,"ywl5320",FORMAT,##__VA_ARGS__);

// 引擎接口
SLObjectItf engineObject = NULL;
SLEngineItf engineEngine = NULL;

//混音器
SLObjectItf outputMixObject = NULL;
SLEnvironmentalReverbItf outputMixEnvironmentalReverb = NULL;
SLEnvironmentalReverbSettings reverbSettings = SL_I3DL2_ENVIRONMENT_PRESET_STONECORRIDOR;


//pcm
SLObjectItf pcmPlayerObject = NULL;
SLPlayItf pcmPlayerPlay = NULL;
SLVolumeItf pcmPlayerVolume = NULL;

//缓冲器队列接口
SLAndroidSimpleBufferQueueItf pcmBufferQueue;

FILE *pcmFile;
void *buffer;

uint16_t *out_buffer;

soundtouch::SoundTouch *soundTouch = NULL;
soundtouch::SAMPLETYPE *sampleBuffer = NULL;

uint mSampleSize;//采样个数

void getPcmData(void **pcm)
{
    while(!feof(pcmFile))
    {
        size_t size = fread(out_buffer, 1, 44100 * 2 * 2, pcmFile);
        if(out_buffer != NULL)
        {
            soundTouch->putSamples((const soundtouch::SAMPLETYPE *) out_buffer, size / 2 / 2);
            //注：采样个数 = 字节数量/声道数/单个采样的占用字节数（这里PCM为16位,即为两个字节，故而为2）

            uint num = soundTouch->receiveSamples(sampleBuffer, size / 4);
            if (num == 0){
                continue;
            }else{
                mSampleSize = num;
                *pcm = sampleBuffer;
                break;
            }


        }

    }
}

void pcmBufferCallBack(SLAndroidSimpleBufferQueueItf bf, void * context)
{
    getPcmData(&buffer);
    (*pcmBufferQueue)->Enqueue(pcmBufferQueue, buffer, mSampleSize * 4);//采样个数*通道数*单个采样的占用字节数
}

extern "C"
JNIEXPORT void JNICALL
Java_com_hxw_openslplayer_MainActivity_play(JNIEnv *env, jobject instance,
                                                  jstring url_) {
    const char *url = env->GetStringUTFChars(url_, 0);

    // TODO
    //读取pcm文件
    pcmFile = fopen(url, "r");
    if(pcmFile == NULL)
    {
        LOGE("%s", "fopen file error");
        return;
    }
    out_buffer = (uint16_t *) malloc(44100 * 2 * 2);


    SLresult result;
    //第一步------------------------------------------
    // 创建引擎对象
    slCreateEngine(&engineObject, 0, 0, 0, 0, 0);
    (*engineObject)->Realize(engineObject, SL_BOOLEAN_FALSE);
    (*engineObject)->GetInterface(engineObject, SL_IID_ENGINE, &engineEngine);


    //第二步-------------------------------------------
    // 创建混音器
    const SLInterfaceID mids[1] = {SL_IID_ENVIRONMENTALREVERB};
    const SLboolean mreq[1] = {SL_BOOLEAN_FALSE};
    result = (*engineEngine)->CreateOutputMix(engineEngine, &outputMixObject, 1, mids, mreq);
    (void)result;
    result = (*outputMixObject)->Realize(outputMixObject, SL_BOOLEAN_FALSE);
    (void)result;
    result = (*outputMixObject)->GetInterface(outputMixObject, SL_IID_ENVIRONMENTALREVERB, &outputMixEnvironmentalReverb);
    if (SL_RESULT_SUCCESS == result) {
        result = (*outputMixEnvironmentalReverb)->SetEnvironmentalReverbProperties(
                outputMixEnvironmentalReverb, &reverbSettings);
        (void)result;
    }
    SLDataLocator_OutputMix outputMix = {SL_DATALOCATOR_OUTPUTMIX, outputMixObject};

    // 第三步--------------------------------------------
    // 创建播放器
    SLDataLocator_AndroidSimpleBufferQueue android_queue={SL_DATALOCATOR_ANDROIDSIMPLEBUFFERQUEUE,2};
    SLDataFormat_PCM pcm={
            SL_DATAFORMAT_PCM,//播放pcm格式的数据
            2,//2个声道（立体声）
            SL_SAMPLINGRATE_44_1,//44100hz的频率
            SL_PCMSAMPLEFORMAT_FIXED_16,//位数 16位
            SL_PCMSAMPLEFORMAT_FIXED_16,//和位数一致就行
            SL_SPEAKER_FRONT_LEFT | SL_SPEAKER_FRONT_RIGHT,//立体声（前左前右）
            SL_BYTEORDER_LITTLEENDIAN//结束标志
    };

    SLDataSource slDataSource = {&android_queue, &pcm};
    SLDataSink audioSnk = {&outputMix, NULL};
    const int idLen = 3;
    const SLInterfaceID ids[idLen] = {
            SL_IID_BUFFERQUEUE,
            SL_IID_EFFECTSEND,
            SL_IID_VOLUME};
    const SLboolean req[idLen] = {
            SL_BOOLEAN_TRUE,
            SL_BOOLEAN_TRUE,
            SL_BOOLEAN_TRUE};
    result = (*engineEngine)->CreateAudioPlayer(engineEngine, &pcmPlayerObject, &slDataSource, &audioSnk, idLen, ids, req);
    // 初始化播放器
    (*pcmPlayerObject)->Realize(pcmPlayerObject, SL_BOOLEAN_FALSE);

    //得到接口后调用  获取Player接口
    (*pcmPlayerObject)->GetInterface(pcmPlayerObject, SL_IID_PLAY, &pcmPlayerPlay);

    //第四步---------------------------------------
    // 创建缓冲区和回调函数
    (*pcmPlayerObject)->GetInterface(pcmPlayerObject, SL_IID_BUFFERQUEUE, &pcmBufferQueue);

    //缓冲接口回调
    (*pcmBufferQueue)->RegisterCallback(pcmBufferQueue, pcmBufferCallBack, NULL);
    //获取音量接口
    (*pcmPlayerObject)->GetInterface(pcmPlayerObject, SL_IID_VOLUME, &pcmPlayerVolume);

    //第五步----------------------------------------
    // 设置播放状态
    (*pcmPlayerPlay)->SetPlayState(pcmPlayerPlay, SL_PLAYSTATE_PLAYING);

    sampleBuffer = static_cast<soundtouch::SAMPLETYPE *>(malloc(44100 * 2 * 2));

    soundTouch = new soundtouch::SoundTouch();
    soundTouch->setSampleRate(44100);//采样率
    soundTouch->setChannels(2);//声道数
    soundTouch->setTempo(2);//播放速度为2倍
//    soundTouch->setPitch(1.5);//声调修改为1.5倍速的声调

    //第六步----------------------------------------
    // 主动调用回调函数开始播放
    pcmBufferCallBack(pcmBufferQueue, NULL);

    env->ReleaseStringUTFChars(url_, url);
}
extern "C"
JNIEXPORT void JNICALL
Java_com_hxw_openslplayer_MainActivity_pause__Z(JNIEnv *env, jobject thiz, jboolean is_pause) {
    if (is_pause){
        //暂停播放
        (*pcmPlayerPlay)->SetPlayState(pcmPlayerPlay, SL_PLAYSTATE_PAUSED);
    }else{
        //恢复播放
        (*pcmPlayerPlay)->SetPlayState(pcmPlayerPlay, SL_PLAYSTATE_PLAYING);
    }
}
extern "C"
JNIEXPORT void JNICALL
Java_com_hxw_openslplayer_MainActivity_stop(JNIEnv *env, jobject thiz) {
    //停止播放
    (*pcmPlayerPlay)->SetPlayState(pcmPlayerPlay, SL_PLAYSTATE_STOPPED);
}
extern "C"
JNIEXPORT void JNICALL
Java_com_hxw_openslplayer_MainActivity_setVolume(JNIEnv *env, jobject thiz, jint percent) {
    (*pcmPlayerVolume)->SetVolumeLevel(
            pcmPlayerVolume,
            (100 - percent) * -50);

}